package com.specter.mvc.model.batis;

import java.beans.Transient;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.specter.utils.StringUtils;

import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * Note: JPA Entity Registry
 * 
 * @author Liang.Wang
 * @version Mar 7, 2017
 */
@Slf4j
public class JPAEntityRegistry {

	private static Map<String, JPAEntity> registry = new HashMap<>();

	private JPAEntityRegistry() {
	}

	public static void add(JPAEntity entity) {
		registry.put(entity.getClassName(), entity);
	}

	/**
	 * 将注解类型解析并添加到内部存储中（类型名称,类型内容的Map）
	 * 
	 * @param annotationedEntityClass 注解的实体类型
	 */
	public static void add(Class<?> annotationedEntityClass) {
		JPAEntity entity = parse(annotationedEntityClass);
		registry.put(annotationedEntityClass.getName(), entity);
	}

	public static Map<String, JPAEntity> get() {
		return registry;
	}

	/**
	 * 将注解的类解析成描述信息
	 * 
	 * @param annotationedEntityClass 注解的实体类型
	 * @return 解析的类信息
	 */
	public static JPAEntity parse(Class<?> annotationedEntityClass) {
		JPAEntity entity = new JPAEntity();
		entity.setClassName(annotationedEntityClass.getName());
		entity.setEntityName(annotationedEntityClass.getSimpleName());

		Table t = annotationedEntityClass.getAnnotation(Table.class);
		if (t != null) {
			entity.setTableName(t.name());
			entity.setTableSchema(t.schema());
		} else {// 没有注解默认生成
			entity.setTableName(StringUtils.toDataName(entity.getEntityName()));
			entity.setTableSchema(null);
		}

		List<JPAEntityProperty> properties = new ArrayList<>();
		try {
			for (Field f : annotationedEntityClass.getDeclaredFields()) {
				if (f.getAnnotation(Transient.class) != null || "serialVersionUID".equals(f.getName())) {// 过滤不被解析的字段
					continue;
				}

				JPAEntityProperty property = new JPAEntityProperty();
				property.setName(f.getName());
				property.setClazz(StringUtils.remove(f.getType().getName(), "java.lang."));

				Column col = f.getAnnotation(Column.class);
				if (col != null) {
					property.setColumn(col.name());
				} else {// 没有注解默认生成
					property.setColumn(StringUtils.toDataName(f.getName()));
				}
				property.setType(f.getType().getSimpleName());// 无法获取以java类型替代
				property.setRemark("field:" + f.getName());// 无法获取以java字段名替代

				if (f.getAnnotation(Id.class) != null) {// Id字段
					entity.setId(property);
				} else {
					properties.add(property);
				}
			}
		} catch (Exception e) {
			log.error(entity.getClass().getSimpleName() + "parse error,please check it !", e);
		}
		entity.setProperties(properties);
		return entity;
	}

	/**
	 * 根据 @Id 注解查找实体的主键值
	 * 
	 * @param entity 数据实体对象
	 * @return 主键值
	 */
	public static <T> Serializable getIdValue(T entity) {
		JPAEntity je = registry.get(entity.getClass().getName());
		try {
			Field f = entity.getClass().getDeclaredField(je.getId().getName());
			f.setAccessible(true);
			return (Serializable) f.get(entity);
		} catch (Exception e) {
			log.error(entity.getClass().getSimpleName() + " There is no property has @Id !", e);
		}
		return null;
	}

	/**
	 * 根据 @Id 注解查找类的主键名
	 * 
	 * @param clazz 数据实体类
	 * @return 主键名
	 */
	public static String getIdName(Class<?> clazz) {
		return registry.get(clazz.getName()).getId().getName();
	}

	/** ====== 数据源对应的 Java Bean ====== **/
	@Data
	public static class JPAEntity {

		private String className;// java类的全名
		private String entityName; // java类的简名

		private String tableName; // 表名
		private String tableSchema; // 库名
		private String tableRemark;// 数据库表注释

		private JPAEntityProperty id;// id主键属性
		private List<JPAEntityProperty> properties;// 属性列表(不含ID字段)

	}

	@Data
	public static class JPAEntityProperty {
		private String name; // 属性名 如: citizenId
		private String clazz; // 属性类名 如: java.lang.String

		private String column; // 表字段名 如: CITIZEN_ID
		private String type; // 表字段类型 如: VARCHAR
		private String remark; // 表字段注释 如: 身份证号

	}
}
